﻿using SoundEngine3;
using SoundEngine3.AudioFile;
using SoundEngine3.AudioProcessing;
using System;
using System.IO;
using SoundEngine3.DspPlugins.Meta;
using System.Collections.Generic;

namespace FlacBoxCodec
{
    public sealed class Mp3FileDescription : AudioFileDescription
    {
        public override string Name
        {
            get { return "Flac"; }
        }

        public override string FilenameType
        {
            get { return "flac"; }
        }

        public override bool SupportDecode
        {
            get
            {
                return true;
            }
        }

        public override bool SupportEncode
        {
            get
            {
                return true;
            }
        }

        public override bool FileSupported(Stream stream)
        {
            // fLaC = 66 4C 61 43
            if (stream.ReadByte() == 0x66 && stream.ReadByte() == 0x4C && stream.ReadByte() == 0x61 && stream.ReadByte() == 0x43)
            {
                return true;
            }

            return false;
        }

        public override void GetSettings(List<ParameterProperties> settings)
        {

        }

        public override IAudioDescription SetupAudioFile(AudioBase audioFile)
        {
            return new WaveDescription { Channels = 2, Depth = 16, SampleRate = 44100 };
        }

        public override AudioFilePathBase CreateInstance()
        {
            return new FlacCodec(this);
        }
    }

    public struct Mp3Description : IAudioDescription
    {
        public byte Channels;
        public int SampleRate;
        public byte Depth;
    }

    public class FlacCodec : AudioFileBase<WaveDescription>
    {
        private WaveFile waveFile;

        public FlacCodec(AudioFileDescription description) : base(description)
        {
        }
        
        public override bool CanStream
        {
            get
            {
                return false;
            }
        }

        public override bool IsRecording
        {
            get
            {
                return waveFile.IsRecording;
            }
        }

        public override bool IsValid
        {
            get
            {
                return waveFile.IsValid;
            }
        }

        public override double Length
        {
            get
            {
                return waveFile.Length;
            }
        }

        public override void Create(Stream stream, IAudioDescription audioDescription)
        {
            Create(stream, (WaveDescription)audioDescription);
        }

        Stream recordstream;
        bool isRecording;

        public override void Create(Stream stream, WaveDescription audioDescription)
        {
            recordstream = stream;

            waveFile = new WaveFile(new WaveFileDescription());
            waveFile.Create(new MemoryStream(), audioDescription);
            isRecording = true;
        }

        public override IAudioDescription CreateAudioDescriptionFromSetup(SoundEngine soundEngine)
        {
            throw new NotImplementedException();
        }
        
        public override void Dispose()
        {
            if (isRecording)
            {
                using (var flacStream= new FlacBox.WaveOverFlacStream(recordstream, FlacBox.WaveOverFlacStreamMode.Encode, false))
                {
                    const int BufferSize = 0xFE00;
                    const int ProgressItemSize = 1000000;
                    byte[] buffer = new byte[BufferSize];
                    int reportedProgress = 0;
                    long copiedBytes = 0;
                    int read;
                    waveFile.BaseStream.Position = 0;

                    while ((read = waveFile.BaseStream.Read(buffer, 0, BufferSize)) > 0)
                    {
                        flacStream.Write(buffer, 0, read);

                        copiedBytes += read;
                        int currentProgress = (int)(copiedBytes / ProgressItemSize);
                        while (currentProgress > reportedProgress)
                        {
                            reportedProgress++;
                        }
                    }

                    waveFile.Dispose();
                }
            }
        }

        public override void OnLoad(Stream stream)
        {
            using (FlacBox.WaveOverFlacStream waveStream = new FlacBox.WaveOverFlacStream(stream, FlacBox.WaveOverFlacStreamMode.Decode, true))
            {
                waveFile = new WaveFile(new WaveFileDescription());
                waveFile.OnLoad(waveStream);

                SourceSampleRate = waveFile.SourceSampleRate;
            }
        }

        public override int Record(Channel left, Channel right, long audioFileSamplePosition, int channelsSampleOffset, int sampleLength)
        {
            int read = waveFile.Record(left, right, audioFileSamplePosition, channelsSampleOffset, sampleLength);
            
            return read;
        }

        public override double GetSample(int channel, int index)
        {
            return waveFile.GetSample(channel, index);
        }
    }
}
